﻿// MIT License

// Copyright (c) 2021-2023 1024


using Kyx.Core.Entities;
using Kyx.Utils;

using Furion;
using Furion.DatabaseAccessor;

using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Diagnostics;
using Microsoft.EntityFrameworkCore.Metadata.Builders;

using System.Linq.Expressions;
using System.Text;

namespace Kyx.EntityFramework.Core
{
    [AppDbContext("ConnectionString")]
    public class DefaultDbContext : AppDbContext<DefaultDbContext>, IModelBuilderFilter
    {
        public DefaultDbContext(DbContextOptions<DefaultDbContext> options) : base(options)
        {
            EnabledEntityChangedListener = true;//启用实体数据更改监听
        }

        /// <summary>
        /// 设置最小日期时间
        /// </summary>
        private DateTimeOffset minDateTimeOffset = Convert.ToDateTime("1900-01-01");

        protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
        {
            // optionsBuilder.UseExceptionProcessor();
            base.OnConfiguring(optionsBuilder);
        }

        //public DbSet<SysLogAudit> AutidLogs { get; set; }

        #region 全局筛选器

        /// <summary>
        /// 构建之后
        /// </summary>
        /// <param name="modelBuilder"></param>
        /// <param name="entityBuilder"></param>
        /// <param name="dbContext"></param>
        /// <param name="dbContextLocator"></param>
        public void OnCreated(ModelBuilder modelBuilder, EntityTypeBuilder entityBuilder, DbContext dbContext, Type dbContextLocator)
        {
            var expression = BuilderIsDeleteLambdaExpression(entityBuilder);
            if (expression == null) return;

            entityBuilder.HasQueryFilter(expression);
        }

        /// <summary>
        /// 构建之前
        /// </summary>
        /// <param name="modelBuilder"></param>
        /// <param name="entityBuilder"></param>
        /// <param name="dbContext"></param>
        /// <param name="dbContextLocator"></param>
        public void OnCreating(ModelBuilder modelBuilder, EntityTypeBuilder entityBuilder, DbContext dbContext, Type dbContextLocator)
        {
        }

        /// <summary>
        /// 构建 u => EF.Property<bool>(u, "IsDeleted") == false 表达式
        /// </summary>
        /// <param name="entityBuilder"></param>
        /// <returns></returns>
        private LambdaExpression BuilderIsDeleteLambdaExpression(EntityTypeBuilder entityBuilder)
        {
            // 获取实体构建器元数据
            var metadata = entityBuilder.Metadata;
            if (metadata.FindProperty(nameof(FullEntity.IsDeleted)) == null) return default;

            // 创建表达式元素
            var parameter = Expression.Parameter(metadata.ClrType, "u");
            var properyName = Expression.Constant(nameof(FullEntity.IsDeleted));
            var propertyValue = Expression.Constant(false);

            // 构建表达式 u => EF.Property<bool>(u, "IsDeleted") == false
            var expressionBody = Expression.Equal(Expression.Call(typeof(EF), nameof(EF.Property), new[] { typeof(bool) }, parameter, properyName), propertyValue);
            var expression = Expression.Lambda(expressionBody, parameter);
            return expression;
        }

        #endregion 全局筛选器

        protected override void SaveChangesFailedEvent(DbContextErrorEventData eventData)
        {
            base.SaveChangesFailedEvent(eventData);
        }

        protected override void SavedChangesEvent(SaveChangesCompletedEventData eventData, int result)
        {
            base.SavedChangesEvent(eventData, result);
        }

        /// <summary>
        /// 重写保存之前事件
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        protected override void SavingChangesEvent(DbContextEventData eventData, InterceptionResult<int> result)
        {
            // 获取当前事件对应上下文
            var dbContext = eventData.Context;
            // 强制重新检查一边实体更改信息
            dbContext!.ChangeTracker.DetectChanges();

            // 通过请求中获取当前操作人
            var userId = App.HttpContext?.GetValue<int>(ClaimConst.Claim_UserId);//
            var userName = App.HttpContext?.GetValue<string>(ClaimConst.Claim_Name);//

            // 获取所有新增和更新的实体
            var entities = dbContext.ChangeTracker.Entries().Where(u =>

//u.Entity.GetType() != typeof(SysLogAudit) && u.Entity.GetType() != typeof(SysLogOp) && u.Entity.GetType() != typeof(SysLogVisit) && u.Entity.GetType() != typeof(SysLog) &&
(u.State == EntityState.Added || u.State == EntityState.Modified || u.State == EntityState.Deleted));

            //审计日志
            //AutidLog(eventData, userId, userName);

            #region 创建人、最后操作人

            {
                //// 获取所有新增和更新的实体
                //var entities = dbContext.ChangeTracker.Entries().Where(u => u.State == EntityState.Added || u.State == EntityState.Modified);

                //foreach (var entity in entities)
                //{
                //    switch (entity.State)
                //    {
                //        // 自动设置租户Id
                //        case EntityState.Added:
                //            entity.Property(nameof(CreateEntity.CreatorUserId)).CurrentValue = userId ?? 0;
                //            entity.Property(nameof(CreateEntity.CreatorUserName)).CurrentValue = userName;
                //            entity.Property(nameof(CreateEntity.CreationTime)).CurrentValue = DateTime.Now;
                //            break;
                //        // 排除租户Id
                //        case EntityState.Modified:
                //            entity.Property(nameof(Entity.TenantId)).IsModified = false;

                //            entity.Property(nameof(FullEntity.LastModifierUserId)).CurrentValue = userId ?? 0;
                //            entity.Property(nameof(FullEntity.LastModifierUserName)).CurrentValue = userName;
                //            entity.Property(nameof(FullEntity.LastModificationTime)).CurrentValue = DateTime.Now;

                //            break;
                //    }
                //}

                //子实体被修改的时候，去这里找旧的值，这样就不用去数据库再找一遍了
                //var deletedcache = entities.Where(d => d.State == EntityState.Deleted).ToDictionary(d => d.Entity.GetType().FullName + d.Property("Id").CurrentValue, d => d);
                foreach (var entity in entities)
                {
                    switch (entity.State)
                    {
                        case EntityState.Added:
                            {
                                //if (entity.Entity is not ICreate creationtimeentity) continue;
                                //if (!entity.Entity.GetType().IsSubclassOf(typeof(ICreate))) continue;
                                if (entity.Entity is ICreate creationtimeentity)
                                {
                                    //var creationtimeentity = entity as ICreate;
                                    //判断创建时间是否小于 定义的最小日期时间，如果是则设置为当前时间
                                    //主要是为了方便有特殊需求需要设置创建时间的情况
                                    if (creationtimeentity.CreationTime < minDateTimeOffset)
                                    {
                                        creationtimeentity.CreationTime = DateTimeOffset.Now;
                                    }
                                    creationtimeentity.CreatorUserId = userId;
                                    creationtimeentity.CreatorUserName = userName!;
                                }
                            }

                            break;

                        case EntityState.Modified:

                            {
                                if (entity.Entity is IDeleted deleted)
                                {
                                    //判断是否是软删除/假删除  如果是假删除 设置删除信息
                                    var isdeleted = entity.Property(nameof(IDeleted.IsDeleted)).CurrentValue;
                                    if ((bool)isdeleted == true)
                                    {
                                        deleted.DeletedTime = DateTimeOffset.Now;
                                        deleted.DeletedUserId = userId;
                                        deleted.DeletedUserName = userName;
                                    }
                                    else
                                    {
                                        //如果是更新，更新信息
                                        if (entity.Entity is IUpdate updatetimeentity)
                                        {
                                            updatetimeentity.UpdateTime = DateTimeOffset.Now;
                                            updatetimeentity.UpdateUserId = userId;
                                            updatetimeentity.UpdateUserName = userName;
                                        }
                                    }
                                }
                                else
                                {
                                    //如果是更新，更新信息
                                    if (entity.Entity is IUpdate updatetimeentity)
                                    {
                                        updatetimeentity.UpdateTime = DateTimeOffset.Now;
                                        updatetimeentity.UpdateUserId = userId;
                                        updatetimeentity.UpdateUserName = userName;
                                    }
                                }

                                if (entity.Entity is ICreate create)
                                {
                                    // 排除创建人
                                    entity.Property(nameof(FullEntity.CreatorUserId)).IsModified = false;
                                    entity.Property(nameof(FullEntity.CreatorUserName)).IsModified = false;
                                    // 排除创建日期
                                    entity.Property(nameof(FullEntity.CreationTime)).IsModified = false;
                                }
                            }
                            break;

                        case EntityState.Deleted:
                            // entity.Property(nameof(FullEntity.DeletionTime)).CurrentValue = DateTime.Now ;
                            //这个是物理删除，不做处理
                            break;
                    }
                }
            }

            #endregion 创建人、最后操作人

            base.SavingChangesEvent(eventData, result);
        }

        /// <summary>
        /// 审计日志
        /// </summary>
        /// <param name="eventData"></param>
        /// <param name="entities"></param>
        /// <param name="userId"></param>
        /// <param name="userName"></param>
        private void AutidLog(DbContextEventData eventData, int userId, string userName)
        {
            #region 审计日志
            {
                try
                {
                    var entities = eventData.Context.ChangeTracker.Entries().Where(u =>
                  u.Entity.GetType() != typeof(SysLogDbAudit) && u.Entity.GetType() != typeof(SysLogOp) && u.Entity.GetType() != typeof(SysLogVis) && u.Entity.GetType() != typeof(SysLogEx) &&
                  (u.State == EntityState.Added || u.State == EntityState.Modified || u.State == EntityState.Deleted || u.State == EntityState.Detached));

                    // 获取所有更改，删除，新增的实体，但排除审计实体（避免死循环）
                    //var entities = dbContext.ChangeTracker.Entries()
                    //    .Where(u => u.Entity.GetType() != typeof(AutidLog) && (u.State == EntityState.Modified ||
                    //                                                           u.State == EntityState.Deleted ||
                    //                                                           u.State == EntityState.Added))
                    //    .ToList();
                    var autidlogs = new List<SysLogDbAudit>();
                    //var changecontent = new StringBuilder();
                    // 获取所有已更改的实体
                    foreach (var entity in entities)
                    {
                        // 获取实体类型
                        var entityType = entity.Entity.GetType();

                        // 获取所有实体有效属性，排除 [NotMapper] 属性
                        var props = entity.OriginalValues.Properties;

                        // 获取实体当前（现在）的值
                        var currentValues = entity.CurrentValues;

                        // 获取数据库中实体的值
                        var databaseValues = entity.GetDatabaseValues();

                        //获取主键的值
                        var key = props.FirstOrDefault(o => o.IsPrimaryKey())?.Name;
                        var keyvalue = currentValues?[key] ?? "";

                        //changecontent.Clear();//遍历前清空
                        //                      // 遍历所有属性
                        //foreach (var prop in props)
                        //{
                        //    // 获取属性名
                        //    var propName = prop.Name;
                        //    // 获取现在的实体值
                        //    var newValue = currentValues[propName];
                        //    object oldValue = null;
                        //    // 如果是新增数据，则 databaseValues 为空，所以需要判断一下
                        //    if (databaseValues != null)
                        //    {
                        //        oldValue = databaseValues[propName];
                        //    }
                        //    //if (newValue == oldValue) continue;
                        //    //if ( newValue.Equals(oldValue)) continue;
                        //    if (newValue == oldValue) continue;

                        //    changecontent.AppendLine($"{propName}: 【{oldValue?.ToString()}】 =>  【{newValue?.ToString()}】");
                        //}

                        var auditLog = new SysLogDbAudit
                        {
                            EntityId = keyvalue.ToString(),
                            TableName = entityType.Name, // 表名
                            CreationTime = DateTime.Now, // 操作时间
                            CreatorUserId = userId, // 操作人
                            CreatorUserName = userName,
                            Operate = entity.State // 操作方式：新增、更新、删除
                        };
                        auditLog.GetDiff(entity);
                        autidlogs.Add(auditLog);
                    }
                    // 插入审计日志表
                    eventData.Context.Set<SysLogDbAudit>().AddRange(autidlogs);
                }
                catch (Exception ex)
                {
                }
            }

            #endregion 审计日志
        }
    }
}